﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Net;
using Actya.Core.Infrastructure;
using Actya.Core.Infrastructure.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Actya.Core.Widgets
{
	public class TwitterWidget : WidgetBase
	{
		[Required]
		[Display(Name = "Title")]
		public string Title { get; set; }

		[Required]
		public DisplayType Type { get; set; }

		[Required]
		[Display(Name = "Username or search term")]
		public string SearchTerm { get; set; }

		[Required]
		[Display(Name = "Number of tweets to display")]
		public int NumberOfTweets { get; set; }

		[Required]
		[Display(Name = "Cache duration of tweets (minutes)")]
		public int CacheDuration { get; set; }

		[JsonIgnore]
		[Display(AutoGenerateField = false)]
		public IEnumerable<Tweet> Tweets { get; private set; }

		public TwitterWidget()
		{
			this.Tweets = new List<Tweet>();
			this.NumberOfTweets = 5;
		}

		public override void Initialize(ICmsContext cmsContext)
		{
			var cacheKey = string.Format("Actya_TwitterWidget_Tweets_{0}", this.SearchTerm);
			var tweets = cmsContext.Host.Cache.Get(cacheKey) as IEnumerable<Tweet>;
			if (tweets == null)
			{
				if (String.IsNullOrEmpty(SearchTerm))
				{
					throw new InvalidOperationException("Unable to retrieve tweets because the user name is not set.");
				}
				// Fetch data
				try
				{
					var twitterClient = new WebClient();
					if (this.Type == DisplayType.User)
					{
						var result =
							twitterClient.DownloadString(
								string.Format("http://api.twitter.com/1/statuses/user_timeline.json?&screen_name={0}&count={1}", this.SearchTerm,
								              this.NumberOfTweets));
						// wrap result array in property, otherwise it cannot be parsed.
						var resultsString = "{ \"results\" : " + result + " }";
						var jsonTweets = JObject.Parse(resultsString);
						tweets = (from tweet in jsonTweets["results"]
						          select new Tweet
						                 	{
												Id = (string)tweet["id_str"],
						                 		Message = (string) tweet["text"],
												User = String.Format("{0}", SearchTerm),
						                 		Timestamp = DateTime.ParseExact((string) tweet["created_at"], "ddd MMM dd HH:mm:ss %zzzz yyyy",
						                 		                                CultureInfo.InvariantCulture,
						                 		                                DateTimeStyles.AssumeUniversal |
						                 		                                DateTimeStyles.AdjustToUniversal)
						                 	}).ToArray();
					}
					else
					{
						var result =
							twitterClient.DownloadString(
								string.Format("http://search.twitter.com/search.json?q={0}&amp;rpp={1}&amp;include_entities=true&amp;result_type=mixed", this.SearchTerm,
											  this.NumberOfTweets));
						var jsonTweets = JObject.Parse(result);
						tweets = (from tweet in jsonTweets["results"]
								  select new Tweet
								  {
									  Id = (string)tweet["id_str"],
									  Message = (string)tweet["text"],
									  User = string.Format("{0}", tweet["from_user"]),
									  Timestamp = DateTime.ParseExact((string)tweet["created_at"], "ddd, dd MMM yyyy HH:mm:ss %zzzz",
																	  CultureInfo.InvariantCulture,
																	  DateTimeStyles.AssumeUniversal |
																	  DateTimeStyles.AdjustToUniversal)
								  }).ToArray();
					}
					cmsContext.Host.Cache.Add(cacheKey, tweets, new TimeSpan(0, 0, this.CacheDuration, 0));
				}
				catch (Exception ex)
				{
					var logger = LogManager.GetLogger(this.GetType());
					logger.Error("Unable to retrieve tweets for {0}: {1}", this.SearchTerm, ex);
				}
			}
			this.Tweets = tweets;
			base.Initialize(cmsContext);
		}

		public enum DisplayType
		{
			User,
			Search
		}

		public class Tweet
		{
			public string Id { get; set; }
			public string Message { get; set; }
			public string User { get; set; }
			public DateTime Timestamp { get; set; }
		}
	}
}
